/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	DateUtil.java
 * 模块说明：	
 * 修改历史：
 * Feb 13, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.util.client;

import java.util.Date;

/**
 * 关于Date类型的工具集
 * <p>
 * 
 * 以下所有方法中涉及到关于Date中各字段取值含义，如无特别说明定义如下： <ol type=disc>
 * <li>{@link #YEAR} - 年份数，例如2009表示2009年。</li>
 * <li>{@link #MONTH} - 1-12之间的月份数。</li>
 * <li>{@link #DAY_OF_MONTH} - 一月内1-31之间的日数。</li>
 * <li>{@link #DAY_OF_WEEK} - 星期，0-6，0表示星期日，1表示星期一，以此类推。</li>
 * <li>{@link #HOUR_OF_DAY} - 0-23之间的小时数。</li>
 * <li>{@link #MINUTE} - 0-59之间的分钟数。</li>
 * <li>{@link #SECOND} - 0-59之间的秒数。</li>
 * <li>{@link #MILLISECOND} - 0-999之间的毫秒数。</li> </ol>
 * 
 * @author lxm
 * 
 */
@SuppressWarnings("deprecation")
public class RDateUtil {

  /** 年 */
  public static final int YEAR = 1;
  /** 月 */
  public static final int MONTH = 2;
  /** 一月中的日 */
  public static final int DAY_OF_MONTH = 5;
  /** 星期 */
  public static final int DAY_OF_WEEK = 7;
  /** 一天中的小时数 */
  public static final int HOUR_OF_DAY = 11;
  /** 分钟 */
  public static final int MINUTE = 12;
  /** 秒 */
  public static final int SECOND = 13;
  /** 毫秒 */
  public static final int MILLISECOND = 14;

  /** 星期日 */
  public static final int SUNDAY = 0;
  /** 星期一 */
  public static final int MONDAY = 1;
  /** 星期二 */
  public static final int TUESDAY = 2;
  /** 星期三 */
  public static final int WEDNESDAY = 3;
  /** 星期四 */
  public static final int THURSDAY = 4;
  /** 星期五 */
  public static final int FRIDAY = 5;
  /** 星期六 */
  public static final int SATURDAY = 6;

  /**
   * 创建一个指定的日期。
   * 
   * @param year
   *          年份，例如2009即表示2009年。
   * @param month
   *          1-12之间的月份数。
   * @param date
   *          一月中1-31之间的某一天。
   * @return
   */
  public static Date newDate(int year, int month, int date) {
    return new Date(year - 1900, month - 1, date);
  }

  /**
   * 创建一个指定时间。返回的Date中日期部分为1900-1-1。
   * 
   * @param hrs
   *          0-23之间的小时数。
   * @param min
   *          0-59之间的分钟数。
   * @return
   */
  public static Date newTime(int hrs, int min) {
    return new Date(0, 0, 1, hrs, min);
  }

  /**
   * 创建一个指定时间。返回的Date中日期部分为1900-1-1。
   * 
   * @param hrs
   *          0-23之间的小时数。
   * @param min
   *          0-59之间的分钟数。
   * @param sec
   *          0-59之间的秒数。
   * @return
   */
  public static Date newTime(int hrs, int min, int sec) {
    return new Date(0, 0, 1, hrs, min, sec);
  }

  /**
   * 创建一个指定时间。返回的Date中日期部分为1900-1-1。
   * 
   * @param hrs
   *          0-23之间的小时数。
   * @param min
   *          0-59之间的分钟数。
   * @param sec
   *          0-59之间的秒数。
   * @param msec
   *          0-999之间的毫秒数。
   * @return
   */
  public static Date newTime(int hrs, int min, int sec, int msec) {
    Date target = newTime(hrs, min, sec);
    set(target, MILLISECOND, msec);
    return target;
  }

  /**
   * 创建一个指定日期+时间。
   * 
   * @param year
   *          年份，例如2009即表示2009年。
   * @param month
   *          1-12之间的月份数。
   * @param date
   *          一月中1-31之间的某一天。
   * @param hrs
   *          0-23之间的小时数。
   * @param min
   *          0-59之间的分钟数。
   * @return
   */
  public static Date newDateTime(int year, int month, int date, int hrs, int min) {
    return new Date(year - 1900, month - 1, date, hrs, min);
  }

  /**
   * 创建一个指定日期+时间。
   * 
   * @param year
   *          年份，例如2009即表示2009年。
   * @param month
   *          1-12之间的月份数。
   * @param date
   *          一月中1-31之间的某一天。
   * @param hrs
   *          0-23之间的小时数。
   * @param min
   *          0-59之间的分钟数。
   * @param sec
   *          0-59之间的秒数。
   * @return
   */
  public static Date newDateTime(int year, int month, int date, int hrs, int min, int sec) {
    return new Date(year - 1900, month - 1, date, hrs, min, sec);
  }

  /**
   * 创建一个指定日期+时间。
   * 
   * @param year
   *          年份，例如2009即表示2009年。
   * @param month
   *          1-12之间的月份数。
   * @param date
   *          一月中1-31之间的某一天。
   * @param hrs
   *          0-23之间的小时数。
   * @param min
   *          0-59之间的分钟数。
   * @param sec
   *          0-59之间的秒数。
   * @param msec
   *          0-999之间的毫秒数。
   * @return
   */
  public static Date newDateTime(int year, int month, int date, int hrs, int min, int sec, int msec) {
    Date target = newDateTime(year, month, date, hrs, min, sec);
    set(target, MILLISECOND, msec);
    return target;
  }

  /**
   * 取得Date的指定字段取值。
   * 
   * @param date
   *          指定的Date。
   * @param field
   *          指定的字段。可能的取值：{@link #YEAR}, {@link #MONTH},
   *          {@link #DAY_OF_MONTH}, {@link #DAY_OF_WEEK} ,
   *          {@link #HOUR_OF_DAY}, {@link #MINUTE},
   *          {@link #SECOND}, {@link #MILLISECOND}。
   * @return
   */
  public static int get(Date date, int field) {
    assert date != null;
    switch (field) {
    case YEAR:
      return date.getYear() + 1900;
    case MONTH:
      return date.getMonth() + 1;
    case DAY_OF_MONTH:
      return date.getDate();
    case DAY_OF_WEEK:
      return date.getDay();
    case HOUR_OF_DAY:
      return date.getHours();
    case MINUTE:
      return date.getMinutes();
    case SECOND:
      return date.getSeconds();
    case MILLISECOND:
      return (int) (date.getTime() % 1000);
    default:
      throw new RuntimeException("无法解释的field(" + field + ")。");
    }
  }

  /**
   * 设置Date的指定字段的值。
   * 
   * @param date
   *          指定的Date。
   * @param field
   *          指定的字段。可能的取值：{@link #YEAR}, {@link #MONTH},
   *          {@link #DAY_OF_MONTH}, {@link #HOUR_OF_DAY} ,
   *          {@link #MINUTE}, {@link #SECOND},
   *          {@link #MILLISECOND}。
   * @param value
   *          取值。
   */
  public static void set(Date date, int field, int value) {
    assert date != null;
    switch (field) {
    case YEAR:
      date.setYear(value - 1900);
      break;
    case MONTH:
      date.setMonth(value - 1);
      break;
    case DAY_OF_MONTH:
      date.setDate(value);
      break;
    case HOUR_OF_DAY:
      date.setHours(value);
      break;
    case MINUTE:
      date.setMinutes(value);
      break;
    case SECOND:
      date.setSeconds(value);
      break;
    case MILLISECOND:
      date.setTime(date.getTime() - date.getTime() % 1000 + value);
      break;
    default:
      throw new RuntimeException("无法解释的field(" + field + ")。");
    }
  }

  /**
   * 截取Date类型值到指定字段。
   * 
   * @param source
   *          输入Date。
   * @param field
   *          指定截取到的字段，可选的字段：{@link #YEAR}, {@link #MONTH},
   *          {@link #DAY_OF_MONTH}, {@link #HOUR_OF_DAY},
   *          {@link #MINUTE}, {@link #SECOND},
   *          {@link #MILLISECOND}。
   * @return
   */
  public static Date truncate(Date source, int field) {
    assert source != null;
    int year = 0;
    int month = 0;
    int date = 1;
    int hrs = 0;
    int min = 0;
    int sec = 0;
    int msec = 0;

    switch (field) {
    case MILLISECOND:
      msec = get(source, MILLISECOND);
    case SECOND:
      sec = source.getSeconds();
    case MINUTE:
      min = source.getMinutes();
    case HOUR_OF_DAY:
      hrs = source.getHours();
    case DAY_OF_MONTH:
      date = source.getDate();
    case MONTH:
      month = source.getMonth();
    case YEAR:
      year = source.getYear();
    }

    Date target = new Date(year, month, date, hrs, min, sec);
    set(target, MILLISECOND, msec);
    return target;
  }

  /**
   * 取得指定Date的日期部分。
   * 
   * @param source
   *          输入的Date。
   * @return
   */
  public static Date datePart(Date source) {
    return truncate(source, DAY_OF_MONTH);
  }

  /**
   * 取得指定Date的时间部分。
   * 
   * @param source
   *          输入的Date。
   * @return 返回值中的日期部分始终是1900-1-1。
   */
  public static Date timePart(Date source) {
    assert source != null;
    return newTime(source.getHours(), source.getMinutes(), source.getSeconds(), get(source,
        MILLISECOND));
  }

  /**
   * 判断指定的日期是否包含时间部分
   * 
   * @param date
   * @return
   */
  public static boolean hasTimePart(Date date) {
    return !date.equals(datePart(date));
  }

  /**
   * 在指定的Date基础上对指定的字段增加或减少指定的量。
   * 
   * @param source
   *          输入的Date。
   * @param field
   *          指定被修改的字段。可能的取值：{@link #YEAR}, {@link #MONTH},
   *          {@link #DAY_OF_MONTH}, {@link #HOUR_OF_DAY},
   *          {@link #MINUTE}, {@link #SECOND},
   *          {@link #MILLISECOND}。
   * @param delta
   *          指定增加或减少的量。正数表示增加；负数表示减少。
   * @return
   */
  public static Date add(Date source, int field, int delta) {
    Date target = new Date();
    target.setTime(source.getTime());
    int oldValue = get(target, field);
    set(target, field, oldValue + delta);
    return target;
  }

  /**
   * 在指定Date基础上增加或减少指定的天数。
   * 
   * @param source
   *          输入的Date。
   * @param days
   *          指定增加或减少的天数。正数表示增加；负数表示减少。
   * @return
   */
  public static Date addDay(Date source, int days) {
    return add(source, DAY_OF_MONTH, days);
  }

  /**
   * 分别将来自于两个Date的日期部分与时间部分拼装成一个新的Date。
   * 
   * @param date
   *          指定提供日期部分的Date。
   * @param time
   *          指定提供时间部分的Date。
   * @return
   */
  public static Date assemble(Date date, Date time) {
    assert date != null;
    assert time != null;
    int msec = get(time, MILLISECOND);
    Date result = new Date(date.getYear(), date.getMonth(), date.getDate(), time.getHours(), time
        .getMinutes(), time.getSeconds());
    set(result, MILLISECOND, msec);
    return result;
  }

  /**
   * 取得指定Date所在月的第一天。
   * 
   * @param source
   *          输入的Date。
   * @return
   */
  public static Date getFirstDayOfMonth(Date source) {
    return truncate(source, MONTH);
  }

  /**
   * 取得指定Date所在月的最后一天。
   * 
   * @param source
   *          输入的Date。
   * @return
   */
  public static Date getLastDayOfMonth(Date source) {
    assert source != null;
    return new Date(source.getYear(), source.getMonth() + 1, 0);
  }

  /**
   * 取得指定Date所在周的指定一天。
   * 
   * @param source
   *          输入的Date。
   * @param dayOfWeek
   *          指定一周中的某一天，取值范围：{@link #SUNDAY}, {@link #MONDAY},
   *          {@link #TUESDAY}, {@link #WEDNESDAY},
   *          {@link #THURSDAY}, {@link #FRIDAY},
   *          {@link #SATURDAY}。
   * @return
   */
  public static Date getDayOfWeek(Date source, int dayOfWeek) {
    assert source != null;
    int day = source.getDay();
    return add(source, DAY_OF_MONTH, dayOfWeek - day);
  }

  /**
   * 判断两个Date中指定字段是否相等。
   * 
   * @param d1
   * @param d2
   * @param field
   *          指定字段，可能的取值：{@link #YEAR}, {@link #MONTH},
   *          {@link #DAY_OF_MONTH}, {@link #DAY_OF_WEEK},
   *          {@link #HOUR_OF_DAY}, {@link #MINUTE},
   *          {@link #SECOND}, {@link #MILLISECOND}。
   * @return
   */
  public static boolean equals(Date d1, Date d2, int field) {
    assert d1 != null;
    assert d2 != null;
    int v1 = get(d1, field);
    int v2 = get(d2, field);
    return v1 == v2;
  }

}
